<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <input type="file" id="upload" />
    <div>已上传进度：<span id="progress"></span></div>
    <div>
      <button id="pause">暂停上传</button>
    </div>

    <script>
      const GlobalPerSize = 0.5 * 1024 * 1024;
      let requestList = [];
      // 断点续传顾名思义即断点 + 续传，所以我们第一步先实现“断点”，也就是暂停上传, 利用xhr的abort方法
      // 我们需要将上传每个切片的 xhr 对象保存起来， requestList
      function request({
        url,
        method = "POST",
        data,
        headers = {},
        onProgress = (e) => e,
        requestList,
      }) {
        return new Promise((resolve, reject) => {
          const xhr = new XMLHttpRequest();
          xhr.upload.onprogress = onProgress;
          xhr.open(method, url);
          Object.keys(headers).map((key) => {
            xhr.setRequestHeader(key, headers[key]);
          });
          xhr.send(data);
          xhr.onload = (e) => {
            // 将请求成的xhr从requestList里面删除
            if (requestList) {
              console.log("请求成功：", xhr);
              // requestList.filter(item=> item === xhr)
              const xhrIndex = requestList.findIndex((item) => item === xhr);
              requestList.splice(xhrIndex, 1);
            }
            resolve({ data: e.target.response });
          };
          // 每次请求开始，将xhr放入数组内
          requestList?.push(xhr);
        });
      }

      const progressDom = document.getElementById("progress");

      let uploadChunkSize = 0;
      const progressHandler = (chunkItem, originFile) => {
        return (e) => {
          uploadChunkSize += e.loaded;
          let percent = uploadChunkSize / originFile.size;
          if (percent > 1) {
            percent = 1;
          }
          progressDom.innerText = percent * 100 + "%";
        };
      };

      document.getElementById("pause").addEventListener("click", () => {
        requestList.forEach((xhr) => xhr?.abort());
        requestList = [];
      });

      document
        .getElementById("upload")
        .addEventListener("change", async (e) => {
          e.preventDefault();
          const originFile = e.target.files[0];
          const fileName = originFile.name.split(".")[0];

          let currentSize = 0;
          const perSize = GlobalPerSize;
          const fileChunkList = [];
          while (currentSize < originFile.size) {
            fileChunkList.push({
              file: originFile.slice(currentSize, currentSize + perSize),
            });
            currentSize += perSize;
          }

          // 3. 将所有的参数数组转换为请求promise数组
          const requestChunkList = fileChunkList
            .map(({ file }, index) => {
              const formData = new FormData();
              formData.append("chunk", file);
              formData.append("fileName", `${fileName}-${index}`);
              return { formData, index };
            })
            .map(({ formData, index }) =>
              request({
                url: "/chunk",
                data: formData,
                onProgress: progressHandler(fileChunkList[index], originFile),
                requestList,
              })
            );

          const results = await Promise.allSettled(requestChunkList);
          const successfulPromises = results.filter(
            (p) => p.status === "fulfilled"
          );
          const failedPromises = results.filter((p) => p.status === "rejected");

          if (failedPromises.length === 0) {
            const mergeRes = await request({
              url: `/merge?filename=${originFile.name}&perSize=${GlobalPerSize}`,
            });
            console.log(1, mergeRes);
          }
        });
    </script>
  </body>
</html>
